#include <Inventor/Qt/SoQt.h>
#include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
#include <Inventor/actions/SoWriteAction.h>
#include <Inventor/nodes/SoEventCallback.h>
#include <Inventor/events/SoKeyboardEvent.h>
#include <Inventor/events/SoMouseButtonEvent.h>
#include <Inventor/draggers/SoDragPointDragger.h>
#include <Inventor/draggers/SoTranslate2Dragger.h>
#include <Inventor/draggers/SoTranslate1Dragger.h>
#include <Inventor/SoOffscreenRenderer.h>



#include <Inventor/actions/SoSearchAction.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoMaterialBinding.h>
#include <Inventor/nodes/SoIndexedFaceSet.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <list>
#include <vector>

#include "halfedges.h"
SoDragPointDragger *pd = NULL;
SoTranslate2Dragger *td = NULL;
SoTranslate1Dragger *t1d = NULL;
SbVec3f scaleFactor;
Mesh he;
SoGroup *mutants  = NULL;
SoIndexedFaceSet *shape = NULL;
SoCoordinate3 *coords   = NULL;
int niterations = 5;

// тут внимание, коэффициенты упакованы для обхода граней по кругу, в то время как в
// моих матрицах для обычной грани a b d c
vector<vector<float> > coeffs;


int pdstate = 1; //1: center; 0:dart-dart 2:kite-kite

void translatePD() {
	if (!pd||!td||!t1d) return;
	if (1==pdstate) {
		pd->translation.setValue(coeffs[1][1]/scaleFactor[0], coeffs[1][0]/scaleFactor[1], coeffs[1][3]/scaleFactor[2]);
	} else {
		if (0==pdstate||2==pdstate) {
			pd->translation.setValue(coeffs[pdstate][2]/scaleFactor[0], coeffs[1][0]/scaleFactor[1], coeffs[pdstate][4]/scaleFactor[2]);
		} else {
			pd->translation.setValue(coeffs[4][0]/scaleFactor[0], coeffs[4][1]/scaleFactor[1], coeffs[4][2]/scaleFactor[2]);
		}
	}
	td->translation.setValue(coeffs[3][1]/scaleFactor[0], coeffs[3][2]/scaleFactor[1], 0);
	t1d->translation.setValue(coeffs[5][0]/scaleFactor[0], 0, 0);
}

const float phi = (1+sqrt(5))/2.0;
void set_c0(float c0) {
	coeffs[1][0] = c0;
	coeffs[1][1] = coeffs[1][3] = 1-phi*phi*c0;
	coeffs[1][2] = 1 - c0 - 2*coeffs[1][1];
}
void set_d1d2(float d1, float d2) {
	coeffs[0][0] = 1/(phi*phi) - 2*d1*sin(36*M_PI/180)*(2*sin(36*M_PI/180)) - 2*d2*sin(54*M_PI/180)/(phi);
	coeffs[0][1] = 1 - coeffs[0][0] - 2*d1 - 2*d2;
	coeffs[0][2] = coeffs[0][3] = d2;
	coeffs[0][4] = coeffs[0][5] = d1;
}
void set_k1k2(float k1, float k2) {
	coeffs[2][0] = 1/phi - 2*k1*sin(36*M_PI/180)*(2*sin(36*M_PI/180)) - k2/(phi*phi);
	coeffs[2][1] = 1 - coeffs[2][0] - 2*k1 - 2*k2;
	coeffs[2][2] = coeffs[2][3] = k2;
	coeffs[2][4] = coeffs[2][5] = k1;
}

void set_dk(float l0, float l1, float l2) {
	float x1 = sin(36*M_PI/180)/phi;
	float y1 = cos(36*M_PI/180)/phi;
	float x2 = cos(18*M_PI/180);
	float y2 = sin(18*M_PI/180);
	float x4 = -cos(18*M_PI/180)/phi;
	float y4 = sin(18*M_PI/180)/phi;
	float x5 = -sin(72*M_PI/180);
	float y5 = 2*pow(sin(36*M_PI/180),2);
	float y = 1/phi;

	float l5 = (y-l0-y1*l1-y2*l2+y4/x4*(x1*l1+x2*l2))/(y5-y4*x5/x4);
	float l4 = -(x1*l1+x2*l2+x5*l5)/x4;
	float l3 = 1 - l0 - l1 - l2 - l4 - l5;
	coeffs[4][0] = l0;
	coeffs[4][1] = l1;
	coeffs[4][2] = l2;
	coeffs[4][3] = l3;
	coeffs[4][4] = l4;
	coeffs[4][5] = l5;
}
void set_star(float l1, float l2) {
	coeffs[3][0] = 1-5*l1-5*l2;
	coeffs[3][1] = l1;
	coeffs[3][2] = l2;
}
void set_sun(float l0) {
	coeffs[5][0] = l0;
	coeffs[5][1] = (1-l0)/10.;
}

void init() {
	coeffs.clear();
	niterations = 2;
	vector<float> center(4,-1); //center
	vector<float> dartdart(6,-1);
	vector<float> kitekite(6,-1);
	vector<float> dartkite(6,-1);





	vector<float> star(3,-1);
	vector<float> sun(2,-1);
/*	vector<float> seven;
	float l0 = 0.447663;
	float l1 = 0.31029;
	float l2 = -0.0199294;
	float x1 = sin(36*M_PI/180)/phi;
	float y1 = cos(36*M_PI/180)/phi;
	float x2 = cos(18*M_PI/180);
	float y2 = sin(18*M_PI/180);
	float x4 = -cos(18*M_PI/180)/phi;
	float y4 = sin(18*M_PI/180)/phi;
	float x5 = -sin(72*M_PI/180);
	float y5 = 2*pow(sin(36*M_PI/180),2);
	float y = 1/phi;

	float l5 = (y-l0-y1*l1-y2*l2+y4/x4*(x1*l1+x2*l2))/(y5-y4*x5/x4);
	float l4 = -(x1*l1+x2*l2+x5*l5)/x4;
	float l3 = 1 - l0 - l1 - l2 - l4 - l5;
//	cerr << l0 << " " << l1 << " " << l2 << " " << l3 << " " << l4 << " " << l5 << endl;
//	cerr << x1*l1 + x2*l2 + x4*l4 + x5*l5 << endl;
//	cerr << l0 + y1*l1 + y2*l2 + y4*l4 + y5*l5 << endl;
	seven.push_back(l0);
	seven.push_back(l1);
	seven.push_back(l2);
	seven.push_back(l3);
	seven.push_back(l4);
	seven.push_back(l5);
	*/

	coeffs.push_back(dartdart);
	coeffs.push_back(center);
	coeffs.push_back(kitekite);
	coeffs.push_back(star);
	coeffs.push_back(dartkite);
	coeffs.push_back(sun);
	set_c0(.24);
	set_d1d2(0.01, 0.24);
	set_k1k2(0.1, 0.1);
	set_dk(0.4, 0.3, 0);
	set_star(-0.08, 0.22);
//	set_star(0.16, -0.15);
	set_sun(.6829);
	translatePD();
}

static char * buffer;
static size_t buffer_size = 0;
static void * buffer_realloc(void * bufptr, size_t size) {
	buffer = (char *)realloc(bufptr, size);
	buffer_size = size;
	return buffer;
}
static SbString buffer_writeaction(SoNode * root) {
	SoOutput out;
	buffer = (char *)malloc(1024);
	buffer_size = 1024;
	out.setBuffer(buffer, buffer_size, buffer_realloc);

	SoWriteAction wa(&out);
	wa.apply(root);

	SbString s(buffer);
	free(buffer);
	return s;
}


bool subdivide(vector<vector<float> > &coeffs) {
	if (!mutants || !shape || !coords) return false;
	mutants->removeAllChildren();
	SoSeparator *res = new SoSeparator();
	SoMaterial *mat = new SoMaterial;
	mat->diffuseColor.set1Value(0, 0.6, 0.6, 0.0);
	mat->diffuseColor.set1Value(1, 0.0, 0.6, 0.0);
	SoMaterialBinding *matbind = new SoMaterialBinding();
	matbind->value = SoMaterialBinding::PER_FACE_INDEXED;
	SoCoordinate3 * c = new SoCoordinate3();
	SoIndexedFaceSet *s = new SoIndexedFaceSet();
	loadShape(shape, coords, he);
	for (int i=0; i<niterations; i++) {
		he.doo_sabin(coeffs);
	}
	unloadShape(s, c, he);
	res->addChild(c);
	res->addChild(mat);
	res->addChild(matbind);
	res->addChild(s);
	mutants->addChild(res);
	return true;

}

void offrender(SoQtExaminerViewer * viewer) {
	subdivide(coeffs);
	SbViewportRegion vp;
	vp.setWindowSize(640,480);
	SoOffscreenRenderer *myRenderer = new SoOffscreenRenderer(vp);//viewer->getViewportRegion());




	if (!myRenderer->render(viewer->getSceneManager()->getSceneGraph())) {
		delete myRenderer;
		exit(-1);
	}

	SbString framefile;
	stringstream ss;

	ss.setf ( std::ios_base::fixed);
	ss.precision(10);
	float minl0 = coeffs[4][0];
	float minl1 = coeffs[4][1];
	float minl2 = coeffs[4][2];
	float mincurv = he.total_curvature();
	float minsun0 = coeffs[5][0];
	float mind2 = coeffs[0][3];
	float mind1 = coeffs[0][5];
	float minc0 = coeffs[1][0];
	float mink2 = coeffs[2][2];
	float mink1 = coeffs[2][4];
	ss << "out/" << mincurv << "_" << minl0 << " " << minl1 << " " << minl2 << " " << mind1 << " " << mind2 << " " << mink1 << " " << mink2 << " " << minc0 << " " << minsun0 << ".png";
	myRenderer->writeToFile(ss.str().c_str(), "png");
	delete myRenderer;
}

float frame=3.9;
SbVec3f pts[] = {SbVec3f(.5625,.1875,.1875), SbVec3f(.19003,.4697,.553841), SbVec3f(.249167,.619071,.0895725), SbVec3f(.5625,.0845737,.332388), SbVec3f(.5625,.0845737,.332388)};
static void event_cb(void * ud, SoEventCallback * n) {
	SoQtExaminerViewer * viewer = (SoQtExaminerViewer *)ud;
	if (SO_KEY_PRESS_EVENT(n->getEvent(), M)) {
		float mincurv = -1;
		/*
		float minl0    = -1;
		float minl1    = -1;
		float minl2    = -1;
		float mind1    = -1;
		float mind2    = -1;
		float mink1    = -1;
		float mink2    = -1;
		float minc0    = -1;
		for (float d1=-1; d1<1; d1+=.2) {
			for (float d2=-1; d2<1; d2+=.2) {
				set_d1d2(d1, d2);
				for (float k1=-1; k1<1; k1+=.2) {
					cerr << k1 << endl;
					for (float k2=-1; k2<1; k2+=.2) {
						set_k1k2(k1, k2);
						for (float l0=-1; l0<1; l0+=.2) {
							for (float l1=-1; l1<1; l1+=.2) {
								for (float l2=-1; l2<1; l2+=.2) {
									set_dk(l0, l1, l2);
									for (float c0=-1; c0<1; c0+=.2) {
										set_c0(c0);
										loadShape(shape, coords, he);
										for (int i=0; i<niterations; i++) {
											he.doo_sabin(coeffs);
										}
										float curv = he.total_curvature();
										if (mincurv<0 || mincurv>curv) {
											mincurv = curv;
											minl0 = l0;
											minl1 = l1;
											minl2 = l2;
											mind1 = d1;
											mind2 = d2;
											mink1 = k1;
											mink2 = k2;
											minc0 = c0;
											cerr << mincurv << ": " << minl0 << " " << minl1 << " " << minl2 << " " << mind1 << " " << mind2 << " " << mink1 << " " << mink2 << " " << minc0 <<  endl;
											offrender(viewer);
										}
									}
								}
							}
						}
					}
				}
			}
		}
		*/
		/*
		float minl0    = -1;
		float minl1    = -1;
		float minl2    = -1;
		float mind1    = -1;
		float mind2    = -1;
		float mink1    = -1;
		float mink2    = -1;
		float minc0    = -1;
		float minstar1 = -1;
		float minstar2 = -1;
		float minsun0  = -1;
					for (float d1=-.1; d1<.6; d1+=.1) {
						for (float d2=-.1; d2<.6; d2+=.1) {
							set_d1d2(d1, d2);
							for (float k1=-.1; k1<.5; k1+=.1) {
			cerr << k1 << endl;
								for (float k2=-.1; k2<.5; k2+=.1) {
									set_k1k2(k1, k2);
		for (float l0=-.2; l0<.8; l0+=.1) {
			for (float l1=-.2; l1<.8; l1+=.1) {
				for (float l2=-.2; l2<.8; l2+=.1) {
					set_dk(l0, l1, l2);
									for (float c0=.3; c0<.35; c0+=.1) {
										set_c0(c0);
										float sun0=.7;
										float star1 = 0.035;
										float star2 = 0.035;
//										for (float sun0=-.1; sun0<1; sun0+=.1) {
//											set_sun(sun0);
//											for (float star1=-.1; star1<1; star1+=.1) {
//												for (float star2=-.9; star2<1; star2+=.1) {
													set_star(star1, star2);
													loadShape(shape, coords, he);
													for (int i=0; i<niterations; i++) {
														he.doo_sabin(coeffs);
													}
													float curv = he.total_curvature();
													if (mincurv<0 || mincurv>curv) {
														mincurv = curv;
														minl0 = l0;
														minl1 = l1;
														minl2 = l2;
														mind1 = d1;
														mind2 = d2;
														mink1 = k1;
														mink2 = k2;
														minc0 = c0;
														minsun0 = sun0;
														minstar1 = star1;
														minstar2 = star2;
														cerr << mincurv << ": " << minl0 << " " << minl1 << " " << minl2 << " " << mind1 << " " << mind2 << " " << mink1 << " " << mink2 << " " << minc0 << " " << minsun0 << " " << minstar1 << " " << minstar2 << endl;
//													}
//												}
//											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		*/
		float minl0    = -1;
		float minl1    = -1;
		float minl2    = -1;
		float oldl0 = coeffs[4][0];
		float oldl1 = coeffs[4][1];
		float oldl2 = coeffs[4][2];
		for (float l0=-1; l0<1; l0+=.1) {
			for (float l1=-1; l1<1; l1+=.1) {
				for (float l2=-1; l2<1; l2+=.1) {
					set_dk(l0, l1, l2);
					loadShape(shape, coords, he);
					for (int i=0; i<niterations; i++) {
						he.doo_sabin(coeffs);
					}
					float curv = he.total_curvature();
					if (mincurv<0 || mincurv>curv) {
						mincurv = curv;
						minl0 = l0;
						minl1 = l1;
						minl2 = l2;
						offrender(viewer);
					}
					cerr << "mincurv: " << mincurv << "\t min: (" << minl0 << ", " << minl1 << ", " << minl2 << ")\t, act: " << l0 << ", " << l1 << ", " << l2 << endl;
				}
			}
		}
		set_dk(minl0, minl1, minl2);
		/*
		float mind1    = -1;
		float mind2    = -1;
		float oldd1 = coeffs[2][5];
		float oldd2 = coeffs[2][3];
		for (float d1=-.1; d1<.7; d1+=.01) {
			for (float d2=-.1; d2<.7; d2+=.01) {
				set_d1d2(d1, d2);
				loadShape(shape, coords, he);
				for (int i=0; i<niterations; i++) {
					he.doo_sabin(coeffs);
				}
				float curv = he.total_curvature();
				if (mincurv<0 || mincurv>curv) {
					mincurv = curv;
					mind1 = d1;
					mind2 = d2;
				}
				cerr << "mincurv: " << mincurv << "\tmind1: " << mind1 << "\td1:" << d1 << "\tmind2: " << mind2 << "\td2:" << d2 << endl;
			}
		}
		set_d1d2(mind1, mind2);
		/*
		float mink1    = -1;
		float mink2    = -1;
		float oldk1 = coeffs[2][5];
		float oldk2 = coeffs[2][3];
		for (float k1=-.1; k1<.5; k1+=.01) {
			for (float k2=-.1; k2<.5; k2+=.01) {
				set_k1k2(k1, k2);
				loadShape(shape, coords, he);
				for (int i=0; i<niterations; i++) {
					he.doo_sabin(coeffs);
				}
				float curv = he.total_curvature();
				if (mincurv<0 || mincurv>curv) {
					mincurv = curv;
					mink1 = k1;
					mink2 = k2;
				}
				cerr << "mincurv: " << mincurv << "\tmink1: " << mink1 << "\tk1:" << k1 << "\tmink2: " << mink2 << "\tk2:" << k2 << endl;
			}
		}
		set_k1k2(oldk1, oldk2);
		*/
		/*

		float oldc0 = coeffs[1][0];
		float minx = -1;
		for (float x=0.1; x<.5; x+=.001) {
			set_c0(x);
			loadShape(shape, coords, he);
			for (int i=0; i<niterations; i++) {
				he.doo_sabin(coeffs);
			}
			float curv = he.total_curvature();
			if (mincurv<0 || mincurv>curv) {
				mincurv = curv;
				minx = x;
			}
			cerr << "mincurv: " << mincurv << "\tminx: " << minx << "\tx:" << x << endl;
											offrender(viewer);
		}
		set_c0(minx);
		*/
		/*

		float oldsun0 = coeffs[5][0];
		float minsun0 = -1;
		for (float sun0=0.4; sun0<.95; sun0+=.001) {
			set_sun(sun0);
			loadShape(shape, coords, he);
			for (int i=0; i<niterations; i++) {
				he.doo_sabin(coeffs);
			}
			float curv = he.total_curvature();
			if (mincurv<0 || mincurv>curv) {
				mincurv = curv;
				minsun0 = sun0;
					offrender(viewer);
			}
			cerr << "mincurv: " << mincurv << "\tminsun0: " << minsun0 << "\tsun0:" << sun0 << endl;
		}
		set_sun(minsun0);
		/*
		float oldstar1 = coeffs[3][1];
		float minstar1 = -1;
		float oldstar2 = coeffs[3][2];
		float minstar2 = -1;
		for (float star1=-0.2; star1<.25; star1+=.01) {
		for (float star2=-0.2; star2<.25; star2+=.01) {
			set_star(star1, star2);
			loadShape(shape, coords, he);
			for (int i=0; i<niterations; i++) {
				he.doo_sabin(coeffs);
			}
			float curv = he.total_curvature();
			if (mincurv<0 || mincurv>curv) {
				mincurv = curv;
				minstar1 = star1;
				minstar2 = star2;
			}
			cerr << "mincurv: " << mincurv << "\tminstar: " << minstar1 << ", " << minstar2<< "\tstar1:" << star1 << endl;
		}
		}
		set_star(minstar1, minstar2);
		*/
		subdivide(coeffs);
		return;
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), R)) {
		frame=0;
		init();
		subdivide(coeffs);
		return;
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), D)) {
		pdstate = 0;
		translatePD();
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), C)) {
		pdstate = 1;
		cerr << pdstate << endl;
		translatePD();
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), K)) {
		pdstate = 2;
		translatePD();
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), X)) {
		pdstate = 4;
		translatePD();
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), N)) {
		int npts =  sizeof(pts)/sizeof(SbVec3f);
		int idx = (int)frame;
		if (idx>=npts-1) return;
		frame+=.05;
		std::cerr << frame << " " << idx << " " << npts << std::endl;
		SbVec3f z = pts[idx] + (frame-idx)*(pts[idx+1]-pts[idx]);
		coeffs[1][0] = z[0];
		coeffs[1][1] = z[1];
		coeffs[1][2] = 1-z[0]-z[1]-z[2];
		coeffs[1][3] = z[2];
		pd->translation.setValue(coeffs[1][1]/scaleFactor[0], coeffs[1][0]/scaleFactor[1], coeffs[1][3]/scaleFactor[2]);
		subdivide(coeffs);
		return;
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), Z)) {
		niterations=7;
		subdivide(coeffs);
		for (int i=8; i>0; i--) {
			for (int j=0; j<30; j++) {
			std::cout << "rendering!\n";
			SbViewportRegion vp;
			vp.setWindowSize(640,480);
			SoOffscreenRenderer *myRenderer = new SoOffscreenRenderer(vp);//viewer->getViewportRegion());




			if (!myRenderer->render(viewer->getSceneManager()->getSceneGraph())) {
				delete myRenderer;
				exit(-1);
			}

			SbString framefile;
			stringstream ss;

			ss.setf ( std::ios_base::fixed);
			ss.precision(10);
			ss << "output_5.00000000000"  << (8-i) << "_" << j<< ".png";
			myRenderer->writeToFile(ss.str().c_str(), "png");
			delete myRenderer;
			std::cerr << "ok: " << ss.str() << "\n";
			}
		niterations--;
			subdivide(coeffs);

		}
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), O)) {
		while (1) {
			std::cout << "rendering!\n";
			SbViewportRegion vp;
			vp.setWindowSize(640,480);
			SoOffscreenRenderer *myRenderer = new SoOffscreenRenderer(vp);//viewer->getViewportRegion());


			int npts =  sizeof(pts)/sizeof(SbVec3f);
			int idx = (int)frame;
			if (idx>=npts-1) return;
			frame+=.005;
			std::cerr << frame << " " << idx << " " << npts << std::endl;
			SbVec3f z = pts[idx] + (frame-idx)*(pts[idx+1]-pts[idx]);
			coeffs[1][0] = z[0];
			coeffs[1][1] = z[1];
			coeffs[1][2] = 1-z[0]-z[1]-z[2];
			coeffs[1][3] = z[2];
			pd->translation.setValue(coeffs[1][1]/scaleFactor[0], coeffs[1][0]/scaleFactor[1], coeffs[1][3]/scaleFactor[2]);


			subdivide(coeffs);


			if (!myRenderer->render(viewer->getSceneManager()->getSceneGraph())) {
				delete myRenderer;
				exit(-1);
			}

			SbString framefile;
			stringstream ss;

			ss.setf ( std::ios_base::fixed);
			ss.precision(10);
			ss << "output_"  << frame << ".png";
			myRenderer->writeToFile(ss.str().c_str(), "png");
			delete myRenderer;
			std::cerr << "ok: " << ss.str() << "\n";
		}
	}

	if (SO_KEY_PRESS_EVENT(n->getEvent(), PAD_ADD)) {
		niterations++;
		subdivide(coeffs);
		return;
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), PAD_SUBTRACT)) {
		niterations--;
		subdivide(coeffs);
		return;
	}
	if (SO_KEY_PRESS_EVENT(n->getEvent(), I)) {
		SbString s = buffer_writeaction(viewer->getSceneGraph());
		stringstream ss;
		ss << "out_a=" << coeffs[1][0] << ",b=" << coeffs[1][1] << ",c=" << coeffs[1][3] << ", f=" << coeffs[0][1] << ",g=" << coeffs[0][2] << ".wrl";
		ofstream outfile;
		outfile.open (ss.str().c_str());
		outfile << "#VRML V1.0 ascii" << endl;
		outfile << s.getString();
		outfile.close();
//		(void)fprintf(stdout, "%s\n", s.getString());
		return;
	}
}

bool extract_pointers(SoSeparator *scene, SoIndexedFaceSet *&shape, SoCoordinate3 *&coords) {
	SoSearchAction sa;
	sa.setType(SoIndexedFaceSet::getClassTypeId());
	sa.setInterest(SoSearchAction::ALL);
	sa.apply(scene);
	SoPathList plist = sa.getPaths();
	int len = plist.getLength();
	cerr << "Searching for IndexedFaceSet node...";
	if (len<=0) {
		cerr << "Could not find SoIndexedFaceSet\n";
		return false;
	}
	cerr << "found.\n";

	SoPath *path = (SoPath*) plist[0];
	shape = (SoIndexedFaceSet *)path->getTail();
	SoGroup *shapeParent = (SoGroup *)path->getNodeFromTail(1);
	cerr << "Searching for Coordinate3 node...";
	sa.setType(SoCoordinate3::getClassTypeId());
	sa.setSearchingAll(FALSE); // don't look under off switches
	sa.setInterest(SoSearchAction::LAST);
	sa.apply(shapeParent);

	path = sa.getPath();
	if (!path) {
		cerr << "Could not find Coordinate3 node!\n";
		return false;
	}
	cerr << "found.\n";
	coords = (SoCoordinate3 *)path->getTail();
	return true;
}

// Callback for the point draggers.
void draggerCB(void *data, SoSensor *sensor) {
	if (!sensor) return;
	SoField *field = ((SoFieldSensor *)sensor)->getAttachedField();
	if (!field) return;
	SbVec3f  trans = ((SoSFVec3f *)field)->getValue();
	trans[0] *= scaleFactor[0]; // Scale down translations
	trans[1] *= scaleFactor[1];
	trans[2] *= scaleFactor[2];
	if (!data) {
		float f = trans[0];
		float g = trans[1];
//		if (f>.5) {
			set_sun(f);
//		} else {
//		set_star(f, g);
//		}
//		coeffs[3][0] = 1-5*f-5*g;
//		coeffs[3][1] = f;
//		coeffs[3][2] = g;
		cerr << " f:" << f << ", g:" << g << endl;
	} else {
		float a = trans[1]; //l0
		float b = trans[0]; //l2
		float c = trans[2]; //l1
		if (1==pdstate) { //center
			set_c0(a);
		cerr << "a: " << a << endl;
		} else  { //kite-kite
			if (2==pdstate) {
				set_k1k2(c, b);
		cerr << " b:" << b << ", c:" << c << endl;
			} else if (0==pdstate) {
				set_d1d2(c, b);
		cerr << " b:" << b << ", c:" << c << endl;
			} else if (4==pdstate) {
			set_dk(trans[0], trans[1], trans[2]);
		cerr << trans[0] << " " << trans[1] << " " << trans[2] << endl;
			}
		}
	}

	subdivide(coeffs);
}

int main(int argc, char *argv[]) {
	init();
	QWidget *myWindow = SoQt::init(argv[0]);
	if (myWindow == NULL) exit(1);

	SoQtExaminerViewer *myViewer = new SoQtExaminerViewer(myWindow);
	SoInput in;
	SoSeparator *root = NULL;

	if (in.openFile("draggers.iv")) {
		root = SoDB::readAll(&in);
		if (!root) {
			return 1;
		}
	}
	root->ref();

	SoSearchAction sa;
	sa.setType(SoScale::getClassTypeId());
	sa.setInterest(SoSearchAction::LAST);
	sa.apply(root);

	SoPath *path = sa.getPath();
	cerr << "Searching for SoScale node...";
	if (!path) {
		cerr << "Could not find SoScale node!\n";
		return 1;
	}
	cerr << "found.\n";
	SoScale *scale = (SoScale *)path->getTail();
	scaleFactor = scale->scaleFactor.getValue();

	sa.setType(SoDragPointDragger::getClassTypeId());
	sa.apply(root);
	path = sa.getPath();
	cerr << "Searching for SoDragPointDragger node...";
	if (!path) {
		cerr << "Could not find SoDragPointDragger node!\n";
		return 1;
	}
	cerr << "found.\n";
	pd = (SoDragPointDragger *)path->getTail();
	SoFieldSensor *fieldsensor = new SoFieldSensor(draggerCB, pd);
	fieldsensor->attach(&pd->translation);

	sa.setType(SoTranslate2Dragger::getClassTypeId());
	sa.apply(root);
	path = sa.getPath();
	cerr << "Searching for SoTranslate2Dragger node...";
	if (!path) {
		cerr << "Could not find SoTranslate2Dragger node!\n";
		return 1;
	}
	cerr << "found.\n";
	td = (SoTranslate2Dragger *)path->getTail();
	SoFieldSensor *fieldsensor2 = new SoFieldSensor(draggerCB, NULL);
	fieldsensor2->attach(&td->translation);

	sa.setType(SoTranslate1Dragger::getClassTypeId());
	sa.apply(root);
	path = sa.getPath();
	cerr << "Searching for SoTranslate1Dragger node...";
	if (!path) {
		cerr << "Could not find SoTranslate1Dragger node!\n";
		return 1;
	}
	cerr << "found.\n";
	t1d = (SoTranslate1Dragger *)path->getTail();
	SoFieldSensor *fieldsensor3 = new SoFieldSensor(draggerCB, NULL);
	fieldsensor3->attach(&t1d->translation);

	SoEventCallback * ecb_k = new SoEventCallback;
	ecb_k->addEventCallback(SoKeyboardEvent::getClassTypeId(), event_cb, myViewer);
	root->addChild(ecb_k);

	SoEventCallback * ecb_m = new SoEventCallback;
	ecb_m->addEventCallback(SoMouseButtonEvent::getClassTypeId(), event_cb, myViewer);
	root->addChild(ecb_m);

	mutants = new SoGroup;
	root->addChild(mutants);

	SoSeparator *model = NULL;
	if (in.openFile(1<argc?argv[argc-1]:"wrl/sun-aceq-jack-aceq.wrl")) {
		model = SoDB::readAll(&in);
		if (!model) {
			return 1;
		}
	}
	model->ref();

	if (!extract_pointers(model, shape, coords)) {
		cerr << "failed to extract pointers" << endl;
		return 1;
	}
	subdivide(coeffs);

	myViewer->setSceneGraph(root);
	myViewer->setTitle("Slider Box");
	myViewer->show();
	SoQt::show(myWindow);
	myViewer->viewAll();
	SoQt::mainLoop();
	model->unref();
	return(0);
}
